import Taro from '@tarojs/taro';
import COS from 'cos-wx-sdk-v5';

// 刷新时间阈值：在密钥到期前 120 秒（2 分钟）开始刷新
const REFRESH_THRESHOLD_SECONDS = 120;

// STS凭证类型：匹配云函数返回的关键字段
interface STSCredentials {
  tmpSecretId: string;
  tmpSecretKey: string;
  sessionToken: string;
  // expiration 是用于判断是否过期的关键，单位应为秒（s）
  expiration: number;
}

// 定义缓存结构
interface CosCache {
  sts: STSCredentials | null;
  instance: COS | null;
  // 正在获取密钥的 Promise，用于实现请求锁
  refreshingPromise: Promise<COS | null> | null;
}

// 模块级别的缓存对象 (相当于单例)
const cosCache: CosCache = {
  sts: null,
  instance: null,
  refreshingPromise: null,
};

/** 纯净的获取 STS 临时密钥 */
const getTempKeys = async (): Promise<STSCredentials | null> => {
  try {
    // 确保在 app.tsx/js 中正确调用了 Taro.cloud.init()
    const res = await Taro.cloud.callFunction({
      name: process.env.TARO_CLOUD_FUNCTION_NAME,
      data: {},
    });
    const result = res.result as { data: STSCredentials };

    // 检查云函数调用是否成功
    if (result && result.data.tmpSecretId) {
      console.log('STS: 成功获取新密钥');
      return result.data;
    }

    // 云函数返回结果不符合预期
    throw new Error('云函数返回结构错误: ' + JSON.stringify(res));
  } catch (err) {
    console.error('STS: 获取临时密钥失败', err);
    // 在这里不进行 setState，让上层调用者（如 getCosInstance）处理错误
    return null;
  }
};

/** 获取或刷新 COS 实例 (可复用的核心工具方法) */
export const getCosInstance = async (): Promise<COS | null> => {
  const nowTimestamp = Math.floor(Date.now() / 1000); // 当前时间戳 (秒)
  const { sts, instance, refreshingPromise } = cosCache;

  // 1. 检查是否有正在进行的刷新请求 (请求锁)
  if (refreshingPromise) {
    console.log('STS: 正在等待其他请求获取新密钥...');
    return refreshingPromise;
  }

  // 2. 检查缓存的 STS 密钥是否有效
  // 密钥存在 AND (过期时间 - 当前时间) > 刷新阈值 (120秒)
  if (
    instance &&
    sts &&
    sts.expiration - nowTimestamp > REFRESH_THRESHOLD_SECONDS
  ) {
    // 密钥依然有效，直接复用
    console.log('STS: 使用缓存的 COS 实例 (剩余有效期 > 120秒)');
    return instance;
  }

  // 3. 密钥过期或不存在，需要重新获取
  console.log('STS: 临时密钥过期/不存在，开始重新获取...');

  // 设置请求锁，防止重复获取
  const refreshTask = (async (): Promise<COS | null> => {
    const newSts = await getTempKeys();

    // 密钥获取失败，清空锁并返回
    if (!newSts) {
      return null;
    }

    // 初始化 COS 实例
    const newCosInstance = new COS({
      SecretId: newSts.tmpSecretId,
      SecretKey: newSts.tmpSecretKey,
      SecurityToken: newSts.sessionToken,
    });

    // 更新缓存
    cosCache.sts = newSts;
    cosCache.instance = newCosInstance;

    return newCosInstance;
  })(); // 立即执行

  // 将 Promise 存入锁中
  cosCache.refreshingPromise = refreshTask;

  try {
    const cosInstance = await refreshTask;
    return cosInstance;
  } finally {
    // 无论成功失败，都必须释放锁
    cosCache.refreshingPromise = null;
  }
};

/** 读取 JSON 文件 */
export const getJsonFile = async <T = any>(key: string): Promise<T | null> => {
  const cosInstance = await getCosInstance();

  if (!cosInstance) {
    // 密钥获取失败，停止操作
    console.error(`COS: 无法获取实例，读取 ${key} 失败`);
    return null;
  }

  try {
    const result = await cosInstance.getObject({
      Bucket: process.env.TARO_BUCKET_NAME,
      Region: process.env.TARO_BUCKET_REGION,
      Key: key,
    });

    // 处理 COS SDK 返回的 Body。
    const bodyString = result.Body.toString();
    const jsonContent = JSON.parse(bodyString);

    console.log(`COS: 成功读取 ${key}`);
    return jsonContent as T;
  } catch (err) {
    console.error(`COS: 读取 ${key} 失败`, err);
    // 这里可以抛出错误或返回 null，取决于您希望上层如何处理
    return null;
  }
};

/** 生成 COS 私有资源的签名 URL */
export const getSignedUrl = async (
  key: string,
  expiresInSeconds: number = 3600 // 默认 1 小时 (3600s)
): Promise<string | null> => {
  const cosInstance = await getCosInstance();

  if (!cosInstance) {
    console.error('COS: 无法获取实例，无法生成签名 URL');
    return null;
  }

  try {
    // 使用 getObjectUrl 方法来生成签名链接
    const url = await cosInstance.getObjectUrl({
      Bucket: process.env.TARO_BUCKET_NAME,
      Region: process.env.TARO_BUCKET_REGION,
      Key: key,
      Sign: true, // **关键步骤：设置为 true 表示生成带签名的 URL**
      Expires: expiresInSeconds, // 链接的有效时间
    });

    // getObjectUrl 返回的结果通常是一个 URL 字符串
    console.log(`COS: 成功为 ${key} 生成签名 URL`);
    return url;
  } catch (err) {
    console.error(`COS: 生成 ${key} 的签名 URL 失败`, err);
    return null;
  }
};
